<?php

/**
 * @file
 * @author Anthony Free
 * @version 20110910
 * @package fwork
 * @subpackage utilities
 */

/**
 * Cryptography Class
 *
 * @author Anthony Free
 * @version 20110910
 * @package fwork
 * @subpackage utilities
 */
class Crypto extends Singleton {
	/**
	 * Constructor for class crpyto
	 */
	public function SingletonConstruct() {
		parent::SingletonConstruct();
	}
	/**
	 * Destructor for class crpyto
	 */
	public function __destruct() {}
	/**
	 * Internal Encrpytion call (in case we/php ever change it)
	 * @param String $string
	 * @param String $salt
	 * @return String
	 */
	private function encrypt($string, $salt) {
		return crypt($string, $salt);
	}
	/**
	 * Encrypt something in md5 using a salt
	 * @param String $string
	 * @param String $ninedigitSalt
	 * @return String
	 */
	public function md5_encrypt($string, $ninedigitSalt) {
		$salt = '$1$'.$ninedigitSalt;
		if(strlen($ninedigitsalt) != 9) {
			throw new InvalidArgumentException("A MD5 Salt MUST be 9 digits in length, it is auto prefixed with $1$");
		} elseif(CRYPT_MD5 != 1) {
			throw new Exception("CRYPT_MD5 is not present");
		} else {
			return $this->encrypt($string, $salt);
		}
	}
	/**
	 * Encrypt with a Standard DES Salt
	 * @param String $string
	 * @param String $twodigitsalt
	 * @return String
	 */
	public function des_std_encrypt($string, $twodigitsalt) {
		if(strlen($twodigitsalt) != 2) {
			throw new InvalidArgumentException("The Length of the salt for Standard DES must be 2");
		} elseif(CRYPT_STD_DES != 1) {
			throw new Exception("CRYPT_STD_DES is not present");
		} else {
			return $this->encrypt($string, $twodigitsalt);
		}
	}
	/**
	 * Encrypt with an Extended DES Salt
	 * @param String $string
	 * @param String $iterationCount  ( Encode #Iterations as 0-63 using: \"./0-9A-Za-z\" )
	 * @param String $fourdigitsalt
	 * @return String
	 */
	public function des_ext_encrypt($string, $iterationCount, $fourdigitsalt) {
		if(strlen($iterationCount) != 4) {
			throw new InvalidArgumentException("The Length of iteration count must be 4. ( Encode #Iterations as 0-63 using: \"./0-9A-Za-z\" )");
		}
		if(strlen($fourdigitsalt) != 4) {
			throw new InvalidArgumentException("The length of the salt for Extended DES must be 4.");
		} elseif(CRYPT_EXT_DES != 1) {
			throw new Exception("CRYPT_EXT_DES is not present");
		} else {
			$salt = "_".$iterationCount.$fourdigitsalt;
			return $this->encrypt($string, $salt);
		}
	}
	/**
	 * Encrypt with a blowfish algorithm
	 * @param String $string
	 * @param String $costParam (base-2 log of the iteration count) must be two digits long and in the range 04-31"
	 * @param String $twentytwodigitsalt
	 */
	public function blowfish_encrypt($string, $costParam, $twentytwodigitsalt) {
		$costParamInt = (int)$costParam;
		if(strlen($costParam) != 2 || $costParamInt > 31 || $costParamInt < 4) {
			throw new InvalidArgumentException("The length of the cost param for blowfish (base-2 log of the iteration count) must be two digits long and in the range \"04\" to \"31\"");
		}
		if(strlen($twentytwodigitsalt) != 22) {
			throw new InvalidArgumentException("The salt for blowfish must be 22 digits in length");
		} elseif(CRYPT_BLOWFISH != 1) {
			throw new Exception("CRYPT_BLOWFISH is not present");
		} else {
			$salt = '$2a$'.$costParam.'$'.$twentytwodigitsalt;
			$this->encrypt($string, $salt);
		}
	}
	/**
	 * Internal Function to encrypt the two sha functions
	 * @param String $string
	 * @param String $type
	 * @param int $rounds
	 * @param String $saltString
	 * @param String $Callee
	 * @return String
	 */
	private function sha_encrypt($string, $type, $rounds, $saltString, $Callee) {
		$roundsInt = (int)$rounds;
		if($roundsInt < 1000 || $roundsInt > 999999999) {
			throw new InvalidArgumentException("Invalid Number of Iterations (rounds must be 1000 - 999,999,999)");
		}
		if(strlen($saltString) < 1) {
			throw new InvalidArgumentException("Salt String is empty in ".$Callee);
		} else {
			$salt = $type.'rounds='.$rounds.'$'.$saltString.'$';
			return $this->encrypt($string, $salt);
		}
	}
	/**
	 * Encrypt using a sha256 algorithm
	 * This function is overloaded, it takes either two, or three arguments. $rounds is optional
	 * @param String $string
	 * @param int $rounds [OPTIONAL]
	 * @param String $salt
	 * @return type
	 * @throws RuntimeException
	 */
	public function sha256_encrypt() {
		// Take care with this overloading if you change it!
		$args = func_get_args();
		$numArgs = count($args);
		if($numArgs == 2) {
			$rounds = 5000;
			$saltStringLocation = 1;
		} elseif($numArgs == 3) {
			$rounds = $args[1];
			$saltStringLocation = 2;
		} else {
			throw new RuntimeException("Invalid number of Arguments passed to ".__FUNCTION__);
		}
		return $this->sha_encrypt($args[0], '$5$', $rounds, $args[$saltStringLocation], __FUNCTION__);
	}
	/**
	 * Encrypt using a sha512 algorithm
	 * This function is overloaded, it takes either two, or three arguments. $rounds is optional
	 * @param String $string
	 * @param int $rounds [OPTIONAL]
	 * @param String $salt
	 * @return type
	 * @throws RuntimeException
	 */
	public function sha512_encrypt() {
		// Take care with this overloading if you change it!
		$args = func_get_args();
		$numArgs = count($args);
		if($numArgs == 2) {
			$rounds = 5000;
			$saltStringLocation = 1;
		} elseif($numArgs == 3) {
			$rounds = $args[1];
			$saltStringLocation = 2;
		} else {
			throw new RuntimeException("Invalid number of Arguments passed to ".__FUNCTION__);
		}
		return $this->sha_encrypt($args[0], '$6$', $rounds, $args[$saltStringLocation], __FUNCTION__);
	}
}
$Crypto = Singleton::CreateInstance('Crypto');
?>